use std::{any::Any, collections::HashMap, sync::Arc};

use async_trait::async_trait;

use common_base::prelude::*;

use crate::ServiceContext;

use super::service_plugin::ServicePlugin;

pub type ServiceComponentCreator = unsafe extern "C" fn() -> *mut Box<dyn ServiceComponent>;

pub enum ExecuteStage {
    Prepare,
    Process,
    Finish,
}

#[async_trait]
pub trait ServiceComponent: Any + Send + Sync {
    fn get_service_component_base(&self) -> &ServiceComponentBase;
    fn get_service_component_base_mut(&mut self) -> &mut ServiceComponentBase;

    async fn run_prepare_plugin_service(&self, srv_ctx: &mut dyn ServiceContext) -> Result<()> {
        self.execute_plugin_service(ExecuteStage::Prepare, srv_ctx)
            .await
    }

    async fn run_process_plugin_service(&self, srv_ctx: &mut dyn ServiceContext) -> Result<()> {
        self.execute_plugin_service(ExecuteStage::Process, srv_ctx)
            .await
    }

    async fn run_finish_plugin_service(&self, srv_ctx: &mut dyn ServiceContext) -> Result<()> {
        self.execute_plugin_service(ExecuteStage::Finish, srv_ctx)
            .await
    }

    async fn execute_plugin_service(
        &self,
        execute_stage: ExecuteStage,
        srv_ctx: &mut dyn ServiceContext,
    ) -> Result<()> {
        for service_plugin in self.get_service_component_base().service_plugin_list.iter() {
            if !service_plugin.get_service_plugin_base().enabled {
                continue;
            }

            // let execute_func: Option<
            //     fn(
            //         &dyn ServicePlugin,
            //         &mut dyn ServiceContext,
            //     ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>>,
            // > = match execute_stage {
            //     ExecuteStage::Prepare => {
            //         if service_plugin.can_prepare_service(srv_ctx) {
            //             Some(ServicePlugin::prepare_service)
            //         } else {
            //             None
            //         }
            //     }
            //     ExecuteStage::Process => {
            //         if service_plugin.can_prepare_service(srv_ctx) {
            //             Some(ServicePlugin::process_service)
            //         } else {
            //             None
            //         }
            //     }
            //     ExecuteStage::Finish => {
            //         if service_plugin.can_prepare_service(srv_ctx) {
            //             Some(ServicePlugin::finish_service)
            //         } else {
            //             None
            //         }
            //     }
            // };

            // if let Some(func) = execute_func {
            //     self::set_rollback_service_list(service_plugin.as_ref(), srv_ctx);

            //     // monitor service plugin start
            //     if let Err(e) = func(service_plugin.as_ref(), srv_ctx).await {
            //         // monitor service plugin finish (exception)
            //         return Err(e);
            //     } else {
            //         // monitor service plugin finish
            //     }
            // }

            match execute_stage {
                ExecuteStage::Prepare => {
                    if service_plugin.can_prepare_service(srv_ctx) {
                        self::set_rollback_service_list(service_plugin.as_ref(), srv_ctx);

                        // monitor service plugin start
                        if let Err(e) =
                            ServicePlugin::prepare_service(service_plugin.as_ref(), srv_ctx).await
                        {
                            // monitor service plugin finish (exception)
                            return Err(e);
                        } else {
                            // monitor service plugin finish
                        }
                    }
                }
                ExecuteStage::Process => {
                    if service_plugin.can_prepare_service(srv_ctx) {
                        self::set_rollback_service_list(service_plugin.as_ref(), srv_ctx);

                        // monitor service plugin start
                        if let Err(e) =
                            ServicePlugin::process_service(service_plugin.as_ref(), srv_ctx).await
                        {
                            // monitor service plugin finish (exception)
                            return Err(e);
                        } else {
                            // monitor service plugin finish
                        }
                    }
                }
                ExecuteStage::Finish => {
                    if service_plugin.can_prepare_service(srv_ctx) {
                        self::set_rollback_service_list(service_plugin.as_ref(), srv_ctx);

                        // monitor service plugin start
                        if let Err(e) =
                            ServicePlugin::finish_service(service_plugin.as_ref(), srv_ctx).await
                        {
                            // monitor service plugin finish (exception)
                            return Err(e);
                        } else {
                            // monitor service plugin finish
                        }
                    }
                }
            }
        }

        Ok(())
    }
}

fn set_rollback_service_list(service_plugin: &dyn ServicePlugin, srv_ctx: &mut dyn ServiceContext) {
    if let Some(rollback_service) = service_plugin
        .get_service_plugin_base()
        .rollback_service
        .as_ref()
    {
        // let rollback_list = srv_ctx.entry("rollbackServiceList").or_insert(Vec::default())
        // if rollback_list.find(rollback_service).is_empty{
        // rollback_list.push(rollback_service)
        // }
    }
}

pub struct ServiceComponentBase {
    pub(crate) id: String,
    pub(crate) name: String,
    pub(crate) property_map: HashMap<String, String>,
    pub(crate) enabled: bool,
    pub(crate) auto_commit: bool,
    pub(crate) prior_service_component: Option<Arc<dyn ServiceComponent>>,
    pub(crate) next_service_component: Option<Arc<dyn ServiceComponent>>,
    pub(crate) service_plugin_list: Vec<Arc<dyn ServicePlugin>>,
    // srv_cntr: &ServiceContainer,
}

impl Default for ServiceComponentBase {
    fn default() -> Self {
        Self {
            id: Default::default(),
            name: Default::default(),
            property_map: Default::default(),
            enabled: Default::default(),
            auto_commit: Default::default(),
            prior_service_component: Default::default(),
            next_service_component: Default::default(),
            service_plugin_list: Default::default(),
        }
    }
}
